# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <math.h>
# include <time.h>

/* 
 *	verifica se um numero eh primo usando o Clivo de Eratostenes
 */
int is_prime(int n)
{
	char *number_list;
	
	unsigned int i, j; 
	unsigned int N, r2n;
	
	int r = 0;
	
	if (n <= 0) return -1;
	if (n == 1) return 1;
	
	N = (unsigned int) n;
	
	number_list = (char *) calloc(N-1, sizeof(char));
	
	if (number_list == NULL)
	{
		printf("\n Erro ao alocar memoria!");
		exit(-1);
	}
	number_list = (char *) memset(number_list, '1', N-1);
	
	r2n = (unsigned int) sqrt(n);
		
	for (i = 2; i <= r2n; i++)
	{
		if (*(number_list + (i-2)) == '0')
			continue;
			
		j = (unsigned int) pow((double) i, 2.0);
		
		do
		{
			*(number_list + (j-2)) = '0';
			
			j += i;
		} 
		while (j <= N);
			
		if (*(number_list + (N-2)) == '0')
			break;
		
	}
	if (*(number_list + (N-2)) == '1') 
		r = 1;
	
	free(number_list);
	
	return r;
}

/*
 * remove os caracteres especiais de um arquivo, incluindo sinais de pontuação
 * mantem apenas caracteres alfanumericos e espaços
 * nao modifica o arquivo original, gera um novo arquivo com o resultado
 */
void pre_processing(const char *input_file, const char *output_file)
{
	FILE *f1, *f2;
	
	char c;
	
	f1 = fopen(input_file, "r");
	
	if (f1 == NULL)
	{
		perror("\n\n Erro ao abrir o arquivo de dados!");
		exit(-2);
	}
	f2 = fopen(output_file, "w");
	
	if (f2 == NULL)
	{
		perror("\n\n Erro ao abrir o arquivo de dados!");
		exit(-2);
	}
	
	while(!feof(f1))
	{
		fread(&c, sizeof(char), 1, f1);
		
		if (isalnum(c) || isspace(c))
		{
			c = tolower(c);
			
			fwrite(&c, sizeof(char), 1, f2);
		}
	}
	fclose(f1);
	fclose(f2);
}

/*
 * remove a primeira palavra de uma frase e inclui outra no final
 */
void shift_phrase(char *s, const char *a)
{	
	char key[] = " ";
	
	char temp[256];
	char *p;
	
	p = strpbrk(s, key);
	
	strcpy(temp, p+1);
	strcat(temp, a);
	strcat(temp, " ");
	strcpy(s, temp);
}

/*
 * remove os espacos em branco de uma frase
 */
void remove_spaces(char *s)
{
	unsigned int i, j = 0;

	for (i = 0; i < strlen(s); i++)
	{
		if (!(isspace(s[i])))
		{
			s[j] = s[i];
			j++;
		}
	}
	s[j] = '\0';
}

/*
 * verifica se uma palavra ou frase eh um palindromo
 */
int is_palindrome(const char *s)
{
	unsigned int size;
	unsigned int i;
	
	int r = 1;
	
	size = strlen(s);
	
	for (i = 0; i < (unsigned int) size/2; i++)
	{
		if (s[i] != s[size-(i+1)])
		{
			r = 0;
			break;
		}		
	}
	return r;		
}

/*
 * calcula a soma dos valores do codigo ASCII dos caracteres de uma string 
 */
unsigned int calculate_ascii_sum(const char *s)
{
	unsigned int sum = 0;
	unsigned int i;
	
	for (i = 0; i < strlen(s); i++)
		sum += (unsigned int) s[i];

	return sum;
}

int main(int argc, char *argv[])
{
	FILE *file;
	
	clock_t t2, t3;						// tempo parcial
	clock_t tt, tt1, tt2 = 0, tt3 = 0;	// tempo total
	
	unsigned int word_palindrome = 0;	// quantidade de palavras palindromo
	unsigned int phrase_palindrome = 0;	// quantidade de frases palindromo
	unsigned int prime_number = 0;		// quantidade de numeros primos

	unsigned int quantity_word = 0;		// total de palavras verificadas
	unsigned int quantity_phrase = 0;	// total de frases verificadas
	
	unsigned int ascii_sum;			// soma do valores ASCII de uma string
	unsigned int num_words;			// quantidades de palavras na frase
	unsigned int size_phrase;		// tamanho atual da frase
	unsigned int max_size_phrase;	// tamnaho maximo de uma frase (argv[2])

	char word[32];			// armazena as palavras lidas do arquivo
	char phrase[512];		// armazena as frases
	char phrase2[512];		// armazena as frases sem espacos entre palavras
	
	char filepath[32];
	
	char type;						// tipo de processamento
									// palavra + n. primo ou palavra + frase	
	int r;

	// verifica a quantidade e os valores dos argumentos
	switch (argc)
	{
		case 2 :
		{
			if (strcmp(argv[1], "-p") != 0)
			{
				printf("\n Erro no tipo do parametro! \n");
				exit(2);
			}
			break;
		}
		case 3 :
		{
			if (strcmp(argv[1], "-f") != 0)
			{
				printf("\n Erro no tipo do parametro! \n");
				exit(2);
			}
			max_size_phrase = strtoul(argv[2], NULL, 10);

			if (max_size_phrase == 0)
			{
				printf("\n Erro no valor do parametro! \n");
				exit(3);
			}
			break;
		}
		default :
		{
			printf("\n Erro na quantidade de parametros! \n");
			exit(1);
		}
	}
	type = argv[1][1];
	// verifica que tipo de processamento deve ser feito
	// seta o caminho para o arquivo correto
	if (type == 'f')
	{
		sprintf(filepath, "../Textos/shakespeare.txt");
		// altera o valor do parametro tamanho da frase 
		// caso o valor seja invalido ou improprio
		if (max_size_phrase < 2)
			max_size_phrase = 2;
		else if (max_size_phrase > 16)
			max_size_phrase = 16;
	}
	else sprintf(filepath, "../Textos/wikipedia.txt");
	
	//sprintf(filepath, "../textos/numeros_primos.txt");

	// marcacao de tempo total
	tt = clock();
	// marcacao de tempo de processamento
	tt1 = clock();
	// faz o pre-processamento do arquivo de entrada
	pre_processing(filepath, "temporary.txt");
	// encerra marcacao de tempo
	tt1 = clock() - tt1;
	
	// abre o arquivo pre-processado
	file = fopen("temporary.txt", "r");
	
	if (file == NULL)
	{
		perror("\n\n Erro ao abrir o arquivo de dados!");
		exit(-2);
	}
	// processamento : palavra + numero primo
	if (type == 'p')
	{
		// leitura do arquivo por palavras
		while (fscanf(file, " %31s", word) != EOF)
		{	
			// verifica se a palavra tem mais de uma letra
			// (palindroms de uma letra sao ignorados)
			if (strlen(word) > 1)
			{
				// marcacao de tempo - palindromo
				t2 = clock();		
				// verifica se eh palindromo
				r = is_palindrome(word);
				// encerra a marcacao de tempo e incrementa tempo total
				t2 = clock() - t2;
				tt2 += t2;
				
				if (r)
				{
					// incrementa total de palindromos
					word_palindrome++;
					// calcula a soma do codigo ASCII da palavra
					ascii_sum = calculate_ascii_sum(word);
					// marcacao de tempo - numero primo
					t3 = clock();
					// verifica se o valor eh um numero primo
					r = is_prime(ascii_sum);
					// encerra a marcacao de tempo e incrementa tempo total
					t3 = clock() - t3;
					tt3 += t3;
					
					if (r)
					{
						// incrementa total de numeros primos
						prime_number++;

//						printf("\n %s  -  %u", word, ascii_sum);
					}
//					else printf("\n %s", word);
				}
				// incrementa total de palavras
				quantity_word++;
			}
		}	
	}
	// processamento : palavra + frase
	else
	{
		while (fscanf(file, " %31s", word) != EOF)
		{	
			if (strlen(word) > 1)
			{
				// marcacao de tempo - palindromo palavra
				t2 = clock();
				
				r = is_palindrome(word);
				
				t2 = clock() - t2;
				tt2 += t2;
				
				if (r)
				{
					word_palindrome++;
					
//					printf("\n %s", word);
				}	
				quantity_word++;
			}
		}
		// volta o ponteiro para o inicio do arquivo
		rewind(file);		
		
		for (size_phrase = 2; size_phrase <= max_size_phrase; size_phrase++)
		{
			// inicia o numero de palavras na frase
			num_words = 0;
			// limpa o conteudo da frase
			phrase[0] = '\0';
			
			while (fscanf(file, " %31s", word) != EOF)
			{	
				// se o numero de palavras na frase for menor que o tamanho atual da frase
				// concatena a palavra no final da frase
				if (num_words < size_phrase)
				{
					strcat(phrase, word);
					strcat(phrase, " ");
					// incrementa o numero de palavras
					num_words++;
				}
				// senao remove a primeira palavra da frase antes da concatenacao
				else 
				{
					shift_phrase(phrase, word);
					
					// copia a frase para uma nova string
					strcpy(phrase2, phrase);
					// remove os espacos da frase
					remove_spaces(phrase2);

					// marcacao de tempo - palindromo frase
					t3 = clock();
					
					r = is_palindrome(phrase2);
					
					t3 = clock() - t3;
					tt3 += t3;
					
					if (r)
					{
						phrase_palindrome++;

//						printf("\n %s", phrase);
					}
					quantity_phrase++;
				}
			}
			rewind(file);
		}
	}
	// fecha o arquivo pre-processado criado
	fclose(file);
	// remove o arquivo
	remove("temporary.txt");

	// encerra a marcacao de tempo
	tt = clock() - tt;

	// impressao das saidas
	printf("\n\n Estatisticas");

	printf("\n\n Quantidade de palavras                : %8u", quantity_word);
	printf("\n Quantidade de palindromos de palavras : %8u", word_palindrome);
	
	if (type == 'p')
	{
		printf("\n\n Quantidade de numeros        : %8u", word_palindrome);
		printf("\n Quantidade de numeros primos : %8u", prime_number);
		printf("\n\n Tempo (encontrar palindromos)     : %9.6f s", (double) tt2 / CLOCKS_PER_SEC);
		printf("\n Tempo (determinar numeros primos) : %9.6f s", (double) tt3 / CLOCKS_PER_SEC);
	}
	else
	{
		printf("\n\n Quantidade de frases                : %8u", quantity_phrase);
		printf("\n Quantidade de palindromos de frases : %8u", phrase_palindrome);
		printf("\n\n Tempo (encontrar palindromos - palavra) : %9.6f s", (double) tt2 / CLOCKS_PER_SEC);
		printf("\n Tempo (encontrar palindromos - frase)   : %9.6f s", (double) tt3 / CLOCKS_PER_SEC);
	}
	printf("\n\n Tempo (pre-processamento) : %9.6f s", (double) tt1 / CLOCKS_PER_SEC);
	printf("\n Tempo total de execucao   : %9.6f s \n\n", (double) tt / CLOCKS_PER_SEC);

	return 0;
}
